package com.fzy.springsecurity2.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fzy.springsecurity2.bean.TUser;
import com.fzy.springsecurity2.config.RedisConfig;
import com.fzy.springsecurity2.dto.R;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author fzy
 * @since 2024-10-07
 */

@Slf4j
@RestController
@RequestMapping("/tuser")
public class TUserController {

    @GetMapping("hello")
    public R<String> hello() {

        return R.success("hello");
    }

    @GetMapping("prime")
    @PreAuthorize("hasRole('ADMIN')")  // spring-EL 表达式
    public R<String> prime() {

        return R.success("hello Admin");
    }

    @Resource
    AuthenticationConfiguration authenticationConfiguration;

    @Resource
    ObjectMapper objectMapper;

    @Resource
    RedisTemplate<String, String> redisTemplate;

    // 在 controller 处理登录请求
    // 缺点：
    // 1. 无法使用 Spring Security 提供的各种过滤器。因此认证成功/失败处理都不会生效。
    // 2. 无法使用 Spring Security 提供的各种注解。例如 @PreAuthorize 等。
    @PostMapping("login")
    public R<String> login(@RequestBody TUser tUser) {
        AuthenticationManager authenticationManager = null;
        try {
            authenticationManager = authenticationConfiguration.getAuthenticationManager();
        } catch (Exception e) {
            log.error("获取 AuthenticationManager 失败：", e);
            return R.error("登录失败");
        }
        UsernamePasswordAuthenticationToken token =
                UsernamePasswordAuthenticationToken.unauthenticated(tUser.getName(), tUser.getPassword());
        try {
            Authentication authenticateResult = authenticationManager.authenticate(token);
            SecurityContextHolder.getContext().setAuthentication(authenticateResult);

            // 自定义缓存用户信息逻辑
            // 这里选择将用户id 作为 key，用户信息作为 value，存入 redis。
            Object o =  authenticateResult.getPrincipal();
            if(o instanceof TUser user){
                user.setPassword(null);  // 信息脱敏
                String json = objectMapper.writeValueAsString(user);
                redisTemplate.opsForValue().set(String.valueOf(user.getId()), json, RedisConfig.DEFAULT_REDIS_EXPIRE);
            } else {
                log.error("在试图缓存用户信息时，获取到的用户信息不是 TUser 类型");
            }

        } catch (Exception e) {
            SecurityContextHolder.clearContext();
            log.error("认证异常：", e);
            return R.error("登录失败");
        }

        return R.success("login");
    }

}
